package net.wicp.tams.common;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import com.alibaba.fastjson.JSONObject;

import lombok.extern.slf4j.Slf4j;
import net.wicp.tams.common.apiext.CollectionUtil;
import net.wicp.tams.common.apiext.IOUtil;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.constant.Encoding;
import net.wicp.tams.common.constant.PathType;

/**
 * 
 * @author 偏锋书生
 *
 */
@Slf4j
public final class Conf {
	private static final Properties props = new Properties();// IOUtil.fileToProperties("/common-apiext.properties",
																// Conf.class);//
																// 属性配置
	private static final Map<String[], CallbackUpdate> reshBacks = new HashMap<>();// 重新加载配置文件时需要的回调函数,key：关注的属性值组

	public static final String proPatternFomate = "^(BOOT-INF/classes/)?%s([a-zA-z0-9]-*){1,}\\.properties$";

	private static final String[] firstReads = new String[] { "common-apiext.properties",
			"common-spring-autoconfig.properties", "common-connector.properties", "common-metrics.properties",
			"common-hadoop.properties", "common-http.properties", "common-micro.properties", "common-others.properties",
			"common-grpc.properties", "common-jdbc.properties", "common-web.properties", "common-os.properties",
			"common-redis.properties", "common-es-client.properties", "common-kubernetes.properties",
			"common-es-assit.properties", "common-binlog-alone.properties", "common-aws.properties" };

	static {
		// ScheduledExecutorService service = Executors
		// .newSingleThreadScheduledExecutor();
		// 第二个参数为首次执行的延时时间，第三个参数为定时执行的间隔时间
		// service.scheduleAtFixedRate(runnable, 10, 1, TimeUnit.SECONDS);
		// 加文件监听
		String properPattern = String.format(proPatternFomate, "common-");// "^(BOOT-INF/classes/)?common-([a-zA-z0-9]-*){1,}\\.properties$";
		FileAlterationMonitor monitor = new FileAlterationMonitor(1000L);// 每隔1000毫秒扫描一次
		FileFilter fileFilter = new RegexFileFilter(properPattern);
		FileAlterationObserver observer = new FileAlterationObserver(
				new File(PathType.getPath(get("common.apiext.checkpath"))), fileFilter);
		FileListerAdapter listener = new FileListerAdapter();
		observer.addListener(listener);
		monitor.addObserver(observer);
		try {
			monitor.start();
		} catch (Exception e) {
			log.error("文件监控错误", e);
		}
		List<InputStream> userConfs = new ArrayList<>();
		// 加载common-*子项目的属性文件
		List<URL> roots = IOUtil.findHasPackRootPath("net.wicp.tams");
		if (roots.size() == 0) {// 20190423 兼容flink读不到配置问题，就是那个经过定义化的classload
			for (String firstRead : firstReads) {
				InputStream commonInput = IOUtil.fileToInputStream("/" + firstRead, Conf.class);
				if (commonInput != null) {
					userConfs.add(commonInput);
				}
			}
		} else {
			for (URL root : roots) {
				List<InputStream>[] findProps = IOUtil.findProps("common-", root, "common-", firstReads);
				for (InputStream file : findProps[0]) {
					try {
						Properties properties = new Properties();// IOUtil.fileToProperties(file);
						properties.load(file);
						overProp(properties);
					} catch (Exception e) {
						log.error("读工具默认配置文件错误", e);
					}
				}
				userConfs.addAll(findProps[1]);
			}
		}

		for (InputStream file : userConfs) {
			try {
				Properties properties = new Properties();// IOUtil.fileToProperties(file);
				properties.load(file);
				overProp(properties);
			} catch (Exception e) {
				log.error("读用户配置文件错误", e);
			}
		}
		log.info("confpropsize:" + props.size());
		// for (Object key : props.keySet()) {
		// String keystr = String.valueOf(key);
		// System.out.println("key======" + keystr);
		// TODO 为什么要设置这个（overProp方法）？会影响阿波罗的取数据，暂去掉 2018-05-16
		// System.setProperty(keystr, props.getProperty(keystr));
		// }
		/////////////////////////////////////// 用户定义的属性文件//////////////////////////////////////////////////////////////////////////
		try {
			Enumeration<URL> confs = Thread.currentThread().getContextClassLoader()
					.getResources("META-INF/tams/auto-config.json");
			while (confs.hasMoreElements()) {
				URL url = confs.nextElement();
				InputStream in = url.openStream();
				String slurp = IOUtil.slurp(in);
				JSONObject parseObject = JSONObject.parseObject(slurp);
				List<URL> findHasPackRootPaths = IOUtil
						.findHasPackRootPath(parseObject.getString("filterpack").split(","));
				for (URL findHasPackRoot : findHasPackRootPaths) {
					List<InputStream>[] findProps = IOUtil.findProps(parseObject.getString("properpre"),
							findHasPackRoot, parseObject.getString("jarpre"), parseObject.getString("firstConfs"));
					for (InputStream file : findProps[0]) {
						try {
							Properties properties = new Properties();// IOUtil.fileToProperties(file);
							properties.load(file);
							overProp(properties);
						} catch (Exception e) {
							log.error("读工具默认配置文件错误", e);
						}
					}
					userConfs.addAll(findProps[1]);
				}
				for (InputStream file : userConfs) {
					try {
						Properties properties = new Properties();// IOUtil.fileToProperties(file);
						properties.load(file);
						overProp(properties);
					} catch (Exception e) {
						log.error("读用户配置文件错误", e);
					}
				}
			}
		} catch (IOException e) {
			log.error("用户配置错误", e);
		}
		////////////////////////// 环境变量处理/////////////////////////////////////////
		Map<String, String> evnconf = System.getenv();
		for (String key : evnconf.keySet()) {
			if (props.containsKey(key)) {
				props.put(key, evnconf.get(key));
			}
		}
		///////////////////////////// 处理系统变量///////////////////////////////////////
		Properties propsSystem = System.getProperties();
		for (Object key : propsSystem.keySet()) {
			if (props.containsKey(key)) {
				props.put(key, propsSystem.get(key));
			}
		}
	}

	private static class FileListerAdapter extends FileAlterationListenerAdaptor {
		@SuppressWarnings("unchecked")
		@Override
		public void onFileChange(File file) {
			final Properties prop = IOUtil.fileToProperties(file);
			List<String[]> selSet = (List<String[]>) CollectionUtil.selectFilter(reshBacks.keySet(), new Predicate() {
				@Override
				public boolean evaluate(Object object) {
					String[] tempary = (String[]) object;
					for (Object obj : prop.keySet()) {
						if (ArrayUtils.contains(tempary, obj)) {
							return true;
						}
					}
					return false;
				}
			});
			for (String[] sel : selSet) {
				reshBacks.get(sel).doReshConf(props, prop);
			}
			Conf.overProp(prop);
		}

		@Override
		public void onFileCreate(File file) {
			final Properties prop = IOUtil.fileToProperties(file);
			Conf.overProp(prop);
		}

	}

	// 默认区域
	private static Locale curLocale = new Locale(get("common.apiext.i18n") == null ? "zh" : get("common.apiext.i18n"));

	@SuppressWarnings("rawtypes")
	public static void addFileFromJar(String propPath, Class classz) {
		Properties newprops = IOUtil.fileToProperties(propPath, classz);
		overProp(newprops);
	}

	public static void addCallBack(CallbackUpdate callbackupdate, String... proNames) {
		Validate.isTrue(ArrayUtils.isNotEmpty(proNames), "必须传要要检查变更的属性值");
		reshBacks.put(proNames, callbackupdate);
	}

	public static void addCallBack(CallbackUpdate callbackupdate, String pre) {
		Validate.isTrue(StringUtils.isNotBlank(pre));
		Map<String, String> preprop = getPre(pre, false);
		Validate.isTrue(MapUtils.isNotEmpty(preprop), "变量没有检查到，请检查[" + pre + "]开始的属性是否有配置");
		addCallBack(callbackupdate, preprop.keySet().toArray(new String[preprop.size()]));
	}

	/***
	 * 通过key得到对应的值
	 * 
	 * @param key key值
	 * @return 对应的值
	 */
	public static String get(String key) {
		if (props.get(key) == null) {
			return null;
		} else {
			String retstr = String.valueOf(props.get(key));
			if ("null".equalsIgnoreCase(retstr)) {
				return null;
			} else {
				return retstr;
			}
		}
	}

	public static Integer getInt(String key) {
		String str = StringUtil.trimSpace(get(key));
		return StringUtil.isNull(str) ? null : Integer.parseInt(str);
	}

	public static Boolean getBoolean(String key) {
		String str = StringUtil.trimSpace(get(key));
		return StringUtil.isNull(str) ? null : Boolean.parseBoolean(str);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <T extends Enum> T getEnum(Class<T> classz, String key) {
		Enum valueOf = Enum.valueOf(classz, StringUtil.trimSpace(get(key)));
		return (T) valueOf;
	}

	public static Double getDouble(String key) {
		String str = StringUtil.trimSpace(get(key));
		return StringUtil.isNull(str) ? null : Double.parseDouble(str);
	}

	/***
	 * 得到指定前缀的所有key及他们对应的值
	 * 
	 * @param key key的前缀
	 * @return 符合条件的结果集
	 */
	public static Map<String, String> getPre(String key, boolean isPure) {
		return CollectionUtil.getPropsByKeypre(props, key, isPure);
	}

	/***
	 * eg: common.apiext.thread.pool.default.coreSize=100
	 * common.apiext.thread.pool.default.maxSize=1000输入
	 * key:common.apiext.thread.pool,输出：default:coreSize=100 default:maxSize=1000
	 * 把前缀key打成不同的组
	 *
	 * @param key
	 * @return
	 */
	public static Map<String, Map<String, String>> getPreGroup(String key) {
		Map<String, Map<String, String>> retmap = new HashMap<String, Map<String, String>>();
		Map<String, String> pres = getPre(key, true);
		// aaa.bbb 模式
		for (String pre : pres.keySet()) {
			int splitIndex = pre.indexOf(".");
			if (splitIndex > 0 && splitIndex < pre.length() - 1) {
				String keyouter = pre.substring(0, splitIndex);
				String keyinner = pre.substring(splitIndex + 1);
				if (!retmap.containsKey(keyouter)) {
					Map<String, String> obj = new HashMap<String, String>();
					retmap.put(keyouter, obj);
				}
				retmap.get(keyouter).put(keyinner, pres.get(pre));
			}
		}
		return retmap;
	}

	public static Properties getPreToProp(String key, boolean isPure) {
		return CollectionUtil.getPropsSubByKeypre(props, key, isPure);
	}

	/***
	 * 替换前缀
	 * 
	 * @param oldPreKey 旧的前缀
	 * @param newPreKey 新的前缀
	 * @return
	 */
	public static Properties replacePre(String oldPreKey, String newPreKey) {
		Properties propsSubByKeypre = CollectionUtil.getPropsSubByKeypre(props, oldPreKey, true);
		Properties retProps = new Properties();
		String pre = String.format("%s.",
				newPreKey.endsWith(".") ? newPreKey.substring(0, newPreKey.length() - 1) : newPreKey);
		for (Object key : propsSubByKeypre.keySet()) {
			retProps.put(String.format(pre + "%s", key), propsSubByKeypre.get(key));
		}
		return retProps;
	}

	/***
	 * 设置当前的Locale
	 * 
	 * @param curLocale 要设置的Locale
	 */
	public static void setCurLocale(Locale curLocale) {
		if (curLocale != null) {
			Conf.curLocale = curLocale;
		}
	}

	/***
	 * 得到当前的Locale
	 * 
	 * @return 当前的Locale
	 */
	public static Locale getCurLocale() {
		return curLocale;
	}

	/***
	 * 得到配置文件的副本，防止配置文件的属性被窜改
	 * 
	 * @return 属性的克隆
	 */
	public static Properties copyProperties() {
		return (Properties) props.clone();
	}

	/***
	 * 添加或者覆盖配置
	 * 
	 * @param initProperties 要覆盖的属性
	 */
	public static void overProp(Properties initProperties) {
		if (initProperties != null && initProperties.size() > 0) {
			for (Object keyobj : initProperties.keySet()) {
				// String keystr = String.valueOf(keyobj);
				props.put(keyobj, initProperties.get(keyobj));
				// System.setProperty(keystr, props.getProperty(keystr)); 2018-05-16
			}
		}
	}

	public static void overJson(JSONObject initjson, String keyPre) {
		if (initjson != null && initjson.size() > 0) {
			keyPre = StringUtil.trimSpace(StringUtil.hasNull(keyPre, ""));
			if (keyPre.length() > 0) {
				keyPre = keyPre.endsWith(".") ? keyPre : keyPre + ".";
			}
			for (String key : initjson.keySet()) {
				props.put(String.format("%s%s", keyPre, key), initjson.get(key));
			}
		}
	}

	public static void overJson(JSONObject initjson) {
		overJson(initjson, null);
	}

	/***
	 * 得到操作系统编码
	 * 
	 * @return 操作系统编码
	 */
	public static Encoding getSystemEncode() {
		return Encoding.getByName(System.getProperty("sun.jnu.encoding"));
	}

	/***
	 * 当前运行java的程序入口 (main方法)所在类主类文件的编码
	 * 
	 * @return 主类文件的编码
	 */
	public static Encoding getFileEncode() {
		return Encoding.getByName(System.getProperty("file.encoding"));
	}

}
